import axios, { AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse, Method } from 'axios';
import { handleNetworkError, handleRequestHeader, handleAuth } from '../utils/tools';
const instance = axios.create({
    baseURL: import.meta.env.VITE_BASE_API,
    // 指定请求超时的毫秒数  
    timeout: 10000,
    // 表示跨域请求时是否需要使用凭证  
    withCredentials: false,
});

// 设置请求头
instance.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
instance.defaults.headers.put['Content-Type'] = 'application/x-www-form-urlencoded';
// instance.defaults.headers.put['Content-Type'] = 'application/json';


// 取消重复请求存储
const pending: Map<string, AbortController> = new Map()

//  格式化数据
const formatData = (config: AxiosRequestConfig) => {
    let url: string = [
        config.url,
        config.method,
    ].join('&')
    if (config.params) {
        url += '&' + JSON.stringify(config.params)
    }
    if (config.data) {
        url += '&' + JSON.stringify(config.data)
    }
    return url
}

// 移除重复请求
const removePending = (config: AxiosRequestConfig) => {
    const url = formatData(config)
    // 当前请求在数组中存在时执行函数体
    if (pending.has(url)) {
        // 执行取消操作
        pending.get(url)?.abort()
        // 从数组中移除记录
        pending.delete(url)
    }
};

// 取消所有请求
export const clearAllPending = () => {
    for (const [, controller] of pending) {
        controller.abort()
    }
    pending.clear()
}

// 取消指定请求
export const clearAssignPending = (url: string, method: string) => {
    for (const key of pending) {
        const _url = key[0]
        // 根据不同的请求分解
        if (~_url.indexOf(`&${method}`) && (_url.split('?')[0] === url || _url.split('&')[0] === url)) {
            pending.get(_url)?.abort()
            pending.delete(_url)
        }
    }
}

// 请求拦截器（发起请求之前的拦截）  
instance.interceptors.request.use(
    (config: InternalAxiosRequestConfig<any>) => {
        removePending(config);
        const controller = new AbortController()
        config.signal = controller.signal
        const url = formatData(config)
        pending.set(url, controller)

        /**  
         * 在这里一般会携带前台的参数发送给后台，比如下面这段代码：  
         * const token = getToken()  
         * if (token) {  
         *  config=handleRequestHeader(config) 
         *  config=handleAuth(config)
         * }  
         */
        return config;
    },
    (error) => {
        return Promise.reject(error);
    },
);


// 响应拦截器（获取到响应时的拦截）  
instance.interceptors.response.use(
    (response: AxiosResponse<any, any>) => {
        const url: string = [
            response.config.url,
            response.config.method,
            JSON.stringify(response.config.params),
            JSON.stringify(response.config.data)
        ].join('&')
        pending.delete(url)
        /**  
         * 根据你的项目实际情况来对 response 和 error 做处理  
         * 这里对 response 和 error 不做任何处理，直接返回  
         */
        return response;
    },
    (error) => {
        // 返回错误原因
        handleNetworkError(error)
        return Promise.reject(error);
    },
);

// 请求成功类型 根据后端返回来修改类型参数
interface ResType<T> {
    code: number;
    data?: T;
    msg?: string;
    message?: string;
    err?: string;
}

// 请求失败类型 根据后端返回来修改类型参数
interface ErrorType<T> {
    code: number;
    data?: T;
    msg?: string;
}

interface Http {
    post<T>(url: string, data?: unknown, params?: AxiosRequestConfig<string> | undefined,): Promise<[ResType<T> | undefined, ErrorType<T> | undefined]>;
    get<T>(url: string, params?: AxiosRequestConfig<string> | undefined): Promise<[ResType<T> | undefined, ErrorType<T> | undefined]>;
    put<T>(url: string, data?: unknown, params?: AxiosRequestConfig<string> | undefined): Promise<[ResType<T> | undefined, ErrorType<T> | undefined]>;
    del<T>(url: string, params?: AxiosRequestConfig<string> | undefined): Promise<[ResType<T> | undefined, ErrorType<T> | undefined]>;
}


interface MyAxiosResponse extends AxiosResponse<any, any> {
    code: number;
}

// 导出常用函数  
const http: Http = {
    post(url, data, params) {
        return new Promise((resolve) => {
            instance
                .post(url, JSON.stringify(data), params)
                .then((res) => {
                    resolve([res.data, undefined]);
                })
                .catch((err) => {
                    resolve([undefined, err]);
                });
        });
    },
    get(url, params) {
        return new Promise((resolve) => {
            instance
                .get(url, params)
                .then((res) => {
                    resolve([res.data, undefined]);
                })
                .catch((err) => {
                    resolve([undefined, err]);
                });
        });
    },
    put(url, data, params) {
        return new Promise((resolve) => {
            instance
                .put(url, data, params)
                .then((res) => {
                    resolve([res.data, undefined]);
                })
                .catch((err) => {
                    resolve([undefined, err]);
                });
        });
    },
    del(url, params) {
        return new Promise((resolve) => {
            instance
                .delete(url, params)
                .then((res) => {
                    resolve([res.data, undefined]);
                })
                .catch((err) => {
                    resolve([undefined, err]);
                });
        });
    }
}

export default http;
